public class Test {

    public static void main(String[] args) throws Exception {
        TaskNotifer n1 = new TaskNotifer();
        Thread t1 = new Thread(() -> {
            System.out.println("t1开始获取任务");
            try {
                n1.prinftInfo();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(1000); // 确保t1先执行
                System.out.println("t2 sleep(1000)完成,开始执行addInfo");
                n1.addInfo("task_001");
            } catch (Exception e) {
                e.printStackTrace();

            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("主线程结束");
    }

}

class TaskNotifer {
    String info = "";

    public synchronized void addInfo(String s) {
        System.out.println("t2 获取到锁，准备添加任务");
        info = s;
        System.out.println("t2 任务添加完成，唤醒等待线程");
        // notify()方法，这个方法会唤醒一个正在this锁等待的线程
        // this.notify();
        this.notifyAll(); // 唤醒所有等待线程
    }

    public synchronized void prinftInfo() throws InterruptedException {
        System.out.println("t1 获取到锁，准备打印任务内容");
        while (info.isEmpty()) {
            System.out.println("t1 内容为空开始等待，释放锁");
            // 必须在synchronized块中才能调用wait()方法
            // wait()方法必须在当前获取的锁对象上调用，这里获取的是this锁
            this.wait();
            System.out.println("t1 被唤醒，重新获取锁,继续执行");
        }
        System.out.println("t1 打印结果： " + info);
    }
}

// wait和notify用于多线程协调运行：

// 在synchronized内部可以调用wait()使线程进入等待状态；
// 必须在已获得的锁对象上调用wait()方法；
// 在synchronized内部可以调用notify()或notifyAll()唤醒其他等待线程；
// 必须在已获得的锁对象上调用notify()或notifyAll()方法；
// 已唤醒的线程还需要重新获得锁后才能继续执行。

// notifyAll()将唤醒所有当前正在this锁等待的线程，
// 而notify()只会唤醒其中一个（具体哪个依赖操作系统，有一定的随机性）。
// 这是因为可能有多个线程正在getTask()方法内部的wait()中等待，使用notifyAll()将一次性全部唤醒。
// 通常来说，notifyAll()更安全。有些时候，如果我们的代码逻辑考虑不周，用notify()会导致只唤醒了一个线程，而其他线程可能永远等待下去醒不过来了。